#include <iostream>
#include <fstream>
#include <string>
#include <stdio.h>
#include <signal.h>
#include <cstring>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstdlib>
#include <unordered_map>
#include <functional>

class Sock;

class Sock
{
public:
    // 创建套接字
    static int GetSock()
    {
        int lisSock = socket(AF_INET, SOCK_STREAM, 0);
        if (lisSock < 0)
            exit(1);

        // 因为服务器断开连接，导致服务器中有很多连接处于 TIME_WAIT 状态，导致服务器无法被重启
        int opt = 1;  // 设置地址复用, 就算服务器崩溃, 也可以马上重启
        setsockopt(lisSock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof opt);
        return lisSock;
    }

    static void Bind(int lisSock, uint16_t port)
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof local); // 初始化置空
        local.sin_family = AF_INET; // 表示需要网络通信
        local.sin_port = htons(port); // 端口号 -> 主机转网络
        local.sin_addr.s_addr = INADDR_ANY;
        // 2.2 本地 socket 写入 socket 对应的内核区域
        if (bind(lisSock, (struct sockaddr*) &local, sizeof local) != 0) 
        { // bind 失败
            exit(0);
        }
    }

    static void Listen(int sock)
    {
        if (listen(sock, 5) < 0)
        {
            exit(3);
        }
    }

    static int Accept(int sock, std::string* cltIp, uint16_t* cltPort)
    {
        // 4. 获取连接, accept 会得到一个新的 socketfd
        struct sockaddr_in clt; // 客户端
        socklen_t len = sizeof clt; // 这个结构体的大小
        // 最后两个参数是输入型也是输出型参数, 收到连接之后会得到 发送方的一些基本数据
        // lisSock, 也就是服务器自身的 sockfd 就是一个接收人的身份, 返回值才是真正的服务员
        int serverSock = accept(sock, (struct sockaddr*) &clt, &len);
        if (serverSock < 0) 
        {
            return -1;
        }
        if (cltPort) *cltPort = ntohs(clt.sin_port);
        if (cltIp)   *cltIp = inet_ntoa(clt.sin_addr);
        return serverSock;
    }

};